/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package net.saga.lib.asyncserver;

import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousByteChannel;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author summers
 */
public abstract class AbstractConnectionState implements ConnectionState {

    //Injected by factory
    protected AsynchronousByteChannel channel;
    
    protected final ByteBuffer readBuffer = ByteBuffer.allocate(4096);
    protected final ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
    private static final Logger LOG = Logger.getLogger(AbstractConnectionState.class.getName());

    protected abstract ConnectionState runImplementation() throws Exception;

    @Override    
    public ConnectionState run() {
        try {
            return  runImplementation();
        } catch (Exception ex) {
            LOG.log(Level.SEVERE, "An exception happened durh", ex);
            if (ex.getMessage() != null) {
                writeln(ex.getMessage());
            }
            return null;
        }
    }

    @Override
    public void cleanup() {
        this.channel = null;
    }

    @Override
    public void setChannel(AsynchronousByteChannel channel) {
        this.channel = channel;
    }

    
    
    /**
     *
     * Since all writes should transition to a read state. No checking is done
     * here to ensure a connection is alive.
     *
     * @param toWrite
     */
    protected void write(String toWrite) {
        try {
            byte[] byteArray = toWrite.getBytes("UTF-8");
            this.write(byteArray);
        } catch (UnsupportedEncodingException ex) {
            LOG.log(Level.SEVERE, null, ex);
            throw new IllegalStateException(ex);
        }

    }

    protected void write(byte[] byteArray) {
        try {
            writeBuffer.clear();
            writeBuffer.put(byteArray);
            writeBuffer.flip();
            channel.write(writeBuffer).get();
        } catch (InterruptedException | ExecutionException ex) {
            throw new LoggingIllegalStateException(ex);
        }
    }

    protected void writeln(byte[] byteArray) {
        write(byteArray);
        write("\n");
    }

    protected void writeln(String string) {
        write(string);
        write("\n");
    }
    
    protected AbstractConnectionState leftShift(String string) {
        this.write(string);
        return this;
    }
    
    protected AbstractConnectionState leftShift(byte[] string) {
        this.write(string);
        return this;
    }
    
    
    /**
     *
     * @return An Array of bytes read
     * @throws ConnectException if the connection has been closed
     */
    protected byte[] read() throws ConnectException {
        while (channel.isOpen()) {
            try {
                readBuffer.clear();
                int length = channel.read(readBuffer).get();
                readBuffer.flip();

                if (length == -1) {
                    throw new ConnectException("User connection is closed");
                }

                byte[] returnArray = new byte[length];
                readBuffer.get(returnArray);

                return returnArray;
            } catch (InterruptedException | ExecutionException ex) {
                LOG.log(Level.SEVERE, null, ex);
                throw new IllegalStateException(ex);
            }
        }
        throw new ConnectException("User connection is closed");
    }

 /**
     *
     * @return An Array of bytes read
     * @throws ConnectException if the connection has been closed
     */
    protected String readString() throws ConnectException {
        try {
            return new String(read(), "UTF-8").trim();
        } catch (UnsupportedEncodingException ex) {
            throw new LoggingIllegalStateException(ex);
        }
    }   
    
}